package org.sfx.apps

import java.io.File
import javafx.application.Application
import javafx.event.EventHandler
import javafx.scene.control.TextArea
import javafx.scene.control.TextField
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyEvent
import javafx.scene.layout.Priority
import javafx.scene.layout.VBox
import javafx.scene.Scene
import javafx.stage.Stage
import org.sfx.rich.Enrich._
import org.sfx.util.Interpreter
import javafx.event.EventHandler
import javafx.event.EventHandler
import javafx.event.ActionEvent
import scala.tools.nsc.interpreter.IMain
import scala.tools.nsc.Settings
import java.net.URLClassLoader
import java.io.PrintWriter
import org.sfx.io.NullWriter
import java.io.Writer
import javafx.beans.property.SimpleStringProperty
import scala.tools.nsc.interpreter.JLineCompletion
import javafx.stage.PopupWindow
import javafx.stage.Popup
import javafx.scene.control.Label
import javafx.scene.control.ContextMenu
import javafx.scene.control.MenuItem
import org.sfx.interpreter.CompletionMenu

class Console extends Application {
  var history = List[String]()
  var workingHistory = (""::history).map(new SimpleStringProperty(_))
  var workingHistoryIndex: Int = 0
  
  val interpreterSettings = new Settings() {
    for (url <- getClass().getClassLoader().asInstanceOf[URLClassLoader].getURLs()) {
      this.classpath.append(new File(url.toURI()).toString())
    }
  }
  
  val output = new TextArea() {
    this.getStyleClass().add("sfx-console-output")
    VBox.setVgrow(this, Priority.ALWAYS)
    this.setEditable(false)
  }
  
  val writer = new Writer() {
     override def close(): Unit = Unit
     override def flush(): Unit = Unit
     override def write(data: Array[Char], start: Int, length: Int): Unit = {
       output.appendText(new String(data, start, length))
     }
  }
  
  val interpreter = new IMain(interpreterSettings, new PrintWriter(writer)) {
  }
  
  val input: TextField = new TextField() {
    this.getStyleClass().add("sfx-console-input")
    this.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler[KeyEvent]() {
      override def handle(event: KeyEvent): Unit = {
        event.getCode() match {
          case KeyCode.UP => {
            event.consume()
            val newWorkingHistoryIndex = workingHistoryIndex + 1
            if (newWorkingHistoryIndex < workingHistory.length) {
              input.textProperty().unbindBidirectional(workingHistory(workingHistoryIndex))
              workingHistoryIndex = newWorkingHistoryIndex
              input.textProperty().unbind()
              input.textProperty().bindBidirectional(workingHistory(workingHistoryIndex))
            } else {
              println("no more history")
            }
          }
          case KeyCode.DOWN => {
            event.consume()
            val newWorkingHistoryIndex = workingHistoryIndex - 1
            if (newWorkingHistoryIndex >= 0) {
              input.textProperty().unbindBidirectional(workingHistory(workingHistoryIndex))
              workingHistoryIndex = newWorkingHistoryIndex
              input.textProperty().bindBidirectional(workingHistory(workingHistoryIndex))
            } else {
              println("no more history")
            }
          }
          case _ =>
        }
      }
    })
    this.setOnKeyPressed(new EventHandler[KeyEvent]() {
      def handle(keyEvent: KeyEvent): Unit = {
        keyEvent.getText() match {
          case "." => runLater {
            val cmp = new JLineCompletion(interpreter)
            val cw = input.textProperty().get().substring(0, input.getSelection().getStart())
            val cwlen = cw.length()
            val m = cmp.completer().complete(cw, cwlen)
            val completionMenu = new CompletionMenu()
            completionMenu.selectFrom(m.candidates) { candidate =>
              input.insertText(input.getSelection().getStart(), candidate)
            }
            completionMenu.show(input.getScene().getWindow())
          }
          case _ =>
        }
      }
    })
    this.setOnAction(new EventHandler[ActionEvent]() {
      def handle(actionEvent: ActionEvent): Unit = {
        val command = input.textProperty().get()
        workingHistoryIndex = 0
        history = command::history
        interpreter.interpret(command)
        input.textProperty().unbindBidirectional(workingHistory(0))
        workingHistory = (""::history).map(new SimpleStringProperty(_))
        input.textProperty().bindBidirectional(workingHistory(0))
      }
    })
  }
  
  def printInterpreterMethods(): Unit = {
    for (method <- interpreter.getClass().methods) {
      println(method)
    }
    for (superClass <- interpreter.getClass().superClass; method <- superClass.methods) {
      println(method)
    }
  }
  
  override def start(stage: Stage): Unit = {
    stage.setTitle("Scala Console")
    
    val root = new VBox() {
      this.getChildren().addAll(output, input)
    }
    
    val scene = new Scene(root, 200, 200) {
      this.getStylesheets().addAll(new File("org/sfx/resources/basics.css").toURI().toString())
    }
    
    stage.setScene(scene)
    interpreter.bind("interpreter", interpreter)
    stage.show()
    input.textProperty().bindBidirectional(workingHistory(0))
    input.requestFocus()
  }
}

object Console {
  def main(args: Array[String]): Unit = {
    Application.launch(classOf[Console], args)
  }
}
